package se581.banker.service;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;
import org.apache.log4j.Logger;

import se581.banker.BankerConstants;
import se581.banker.listener.HealthMonitorListener;
import se581.banker.util.Queue;
import se581.banker.util.ServerSocketInfo;



/**
 * This will monitor the health of the UpdateTracker instances
 * and notifies the RegistrationService if it finds it unresponsive or slow. 
 * Server health is determined using the UpdateTracker.  If the UpdateTracker
 * is down, it is assumed that the InquiryTracker is also down.  Both are always
 * failed over together. 
 * 
 * This service creates a Listener thread for each server instance that is being monitored.
 * 
 * @author mmichalak
 *
 */
public class HealthMonitorService {
	static Logger logger = Logger.getLogger(HealthMonitorService.class.getName());
	static final int NUM_PING_RETRIES = 2;
	
	RegistrationService regSvc = null;

	Map<ServerSocketInfo,String> serverStatus = null;
	Map<ServerSocketInfo, Thread> listenerThreads = null;
	
	/**
	 * Variable set by the Listener thread indicating the response 
	 * from the update service
	 */
	Queue<ServerSocketInfo> pingResponse = new Queue<ServerSocketInfo>();
	
	
	/**
	 * Method TBD if Tracker is determined down.
	 * Must notify RegSvc and then failover...
	 */
	
	public void startHealthService() {
		logger.debug("Starting HealthMonitorService... ");

		
		/*
		 *  Loop to check status returned by Threads
		 */
		boolean isMonitoring = true;
		// Ping Update Tracker every n seconds
		while (isMonitoring) {	
			logger.debug("Waking to check for failures....");
			try {
				
				// Anything in pingResponse queue is BAD...means server problems
				while (!pingResponse.isEmpty())  {
					
					ServerSocketInfo failedServerPort = pingResponse.dequeue();
					serverStatus.remove(failedServerPort);
					// THis will take that server out of service 
					regSvc.serverFailure(failedServerPort);
				}
			
				Thread.sleep(BankerConstants.UPDATE_HEARTBEAT_INTERVAL * 1000);
			 } catch (InterruptedException e) {
				 logger.debug("HealthMonitor waking to check Listener status...");
			 }
			 
		}
		
		logger.debug("Exiting....");
	}
	
	
	public void startNewHealthListener(ServerSocketInfo serverPort) {

		logger.debug("Starting HealthMonitor:  " + serverPort);
	
		HealthMonitorListener healthListener = new HealthMonitorListener(serverPort);
		healthListener.setHealthSvc(this);
		
		Thread thr = new Thread(healthListener);
        thr.setDaemon(true);
        thr.start();
        
        listenerThreads.put(serverPort, thr);
        serverStatus.put(serverPort, BankerConstants.OK_STATUS);

	}
	
	
	/**
	 * Socket factory for the Threads
	 * 
	 * @param serverPort = server:port
	 * @return
	 * @throws NumberFormatException
	 * @throws UnknownHostException
	 * @throws IOException
	 */
	public Socket getServerSocket(ServerSocketInfo serverPort) throws NumberFormatException, UnknownHostException, IOException {
		
		return new Socket( serverPort.getHost(), Integer.parseInt(serverPort.getPort()) );
	}


	public RegistrationService getRegSvc() {
		return regSvc;
	}


	public void setRegSvc(RegistrationService regSvc) {
		this.regSvc = regSvc;
	}


	public void setPingResponse(ServerSocketInfo pingResponse) {
		this.pingResponse.enqueue(pingResponse);
	}


	public Map<ServerSocketInfo, String> getServerStatus() {
		return serverStatus;
	}


	public void setServerStatus(Map<ServerSocketInfo, String> serverStatus) {
		this.serverStatus = serverStatus;
	}


	public void setListenerThreads(Map<ServerSocketInfo, Thread> listenerThreads) {
		this.listenerThreads = listenerThreads;
	}
}
